﻿#include "FITKGraphObjectInteractionSurfToSurfExplicit.h"

// VTK
#include <vtkPoints.h>
#include <vtkVertex.h>
#include <vtkCellArray.h>
#include <vtkDoubleArray.h>
#include <vtkPolyData.h>
#include <vtkUnstructuredGrid.h>
#include <vtkPointData.h>
#include <vtkCellData.h>
#include <vtkProperty.h>
#include <vtkGlyph3D.h>
#include <vtkDistanceToCamera.h>
#include <vtkMaskPoints.h>
#include <vtkPointSource.h>
#include <vtkRenderer.h>
#include <vtkCamera.h>
#include <vtkExtractSelection.h>
#include <vtkArrowSource.h>
#include <vtkSelectionNode.h>
#include <vtkSelection.h>

// Graph
#include "FITK_Interface/FITKVTKAlgorithm/FITKGraphActor.h"
#include "FITKGraphCommons.h"
#include "FITK_Interface/FITKVTKAlgorithm/FITKPolygonSource.h"

// Render
#include "FITK_Component/FITKRenderWindowVTK/FITKGraphRender.h"

// Global data
#include "FITK_Kernel/FITKCore/FITKDataRepo.h"

// Data
#include "FITK_Interface/FITKInterfaceStructural/FITKAbaInteractionSurfToSurfExplicit.h"
#include "FITK_Interface/FITKInterfacePhysics/FITKBoundaryLocation.h"
#include "FITK_Interface/FITKInterfacePhysics/FITKInteractionLocation.h"
#include "FITK_Interface/FITKInterfaceModel/FITKAssInstanceTR.h"
#include "FITK_Interface/FITKInterfaceModel/FITKMeshSurfaceElement.h"
#include "FITK_Interface/FITKInterfaceModel/FITKMeshSurfaceNode.h"
#include "FITK_Interface/FITKInterfaceModel/FITKModelSet.h"

// Data manager
#include "FITKSetSurfPolyProvider.h"

namespace Exchange
{
    FITKGraphObjectInteractionSurfToSurfExplicit::FITKGraphObjectInteractionSurfToSurfExplicit(AbaqusData::FITKDataCase* caseData, Interface::FITKAbaInteractionSurfToSurfExplicit* surfToSurfExpData)
        : FITKGraphObjectInteractionBase(caseData, surfToSurfExpData)
    {
        if (!surfToSurfExpData)
        {
            return;
        }

        // Initialize.
        init();

        // Generate the poly data for shape label.
        bool flag = generatePolyData(surfToSurfExpData);
        if (!flag)
        {
            return;
        }

        // Generate tie actor.
        generateGraph();

        // Update the visibility.
        updateVisibility();
    }

    FITKGraphObjectInteractionSurfToSurfExplicit::~FITKGraphObjectInteractionSurfToSurfExplicit()
    {
        // Delete the additional source.
        deleteVtkObj(m_surfaceSource);

        // Delete extractor pointers.
        deleteVtkObj(m_selectNode_2);
        deleteVtkObj(m_section_2);
        deleteVtkObj(m_extractSelection_2);
        deleteVtkObj(m_idArray_2);

        // Delete component grid data.
        deleteVtkObj(m_ugrid_2);
        deleteVtkObj(m_points_2);
        deleteVtkObj(m_modelIdArray_2);
        deleteVtkObj(m_normal);
        deleteVtkObj(m_normal_2);  

        // Reset the pointer.( Deleted in base class. )
        m_glyph = nullptr;
        m_glyph_2 = nullptr;

//#ifdef QT_NO_DEBUG
        m_distanceToCamera = nullptr;
        m_distanceToCamera_2 = nullptr;
//#endif
    }

    void FITKGraphObjectInteractionSurfToSurfExplicit::init()
    {
        this->Superclass::init();

        // Create location two's glyph grid data.
        //@{
        m_ugrid_2 = vtkUnstructuredGrid::New();
        m_points_2 = vtkPoints::New();
        m_ugrid_2->SetPoints(m_points_2);
        m_modelIdArray_2 = vtkIntArray::New();
        m_modelIdArray_2->SetNumberOfComponents(1);

        m_normal = vtkDoubleArray::New();
        m_normal->SetNumberOfComponents(3);

        m_normal_2 = vtkDoubleArray::New();
        m_normal_2->SetNumberOfComponents(3);
        //@}

//#ifdef QT_NO_DEBUG
        if (!m_simplifiedGlyph)
        {
            // Set the glyph size.
            m_glyphSize = 13.;
            m_highlightGlyphSize = 60.;

            // Create the data source.
            FITKPolygonSource* source = FITKPolygonSource::New();
            source->SetGeneratePolygon(false);
            source->SetGeneratePolyline(true);
            source->SetRadius(1);
            source->SetNumberOfSides(4);
            m_polygonSource = source;
        }
        else
        {
            // Set the glyph size.
            m_glyphSize = 15.;

            // Create the data source.
            vtkPointSource* source = vtkPointSource::New();
            source->SetRadius(0);
            source->SetNumberOfPoints(1);
            m_pointSource = source;
        }
//#endif // QT_NO_DEBUG

        // Initialize the second extractor for location two.
        //@{
        m_selectNode_2 = vtkSelectionNode::New();
        m_section_2 = vtkSelection::New();
        m_extractSelection_2 = vtkExtractSelection::New();
        m_selectNode_2->SetContentType(vtkSelectionNode::INDICES);
        m_selectNode_2->SetFieldType(vtkSelectionNode::SelectionField::CELL);
        m_section_2->AddNode(m_selectNode_2);
        m_extractSelection_2->SetInputData(1, m_section_2);

        m_idArray_2 = vtkIdTypeArray::New();
        m_selectNode_2->SetSelectionList(m_idArray_2);
        m_extractSelection_2->SetInputData(m_ugrid_2);
        //@}

        // Create the source for highlighting surface's contact.
        m_surfaceSource = vtkArrowSource::New();
        m_surfaceSource->SetShaftRadius(0);
        m_surfaceSource->SetTipLength(0.2);
        m_surfaceSource->SetTipRadius(0.1);
        m_surfaceSource->SetTipResolution(m_sourceResolution);
    }

    void FITKGraphObjectInteractionSurfToSurfExplicit::initActors()
    {
        // Initialize location one's actor.
        this->Superclass::initActors();

        // Initialize location two's actor.
        //@{
        // Get the interactions or constraint color.
        double color3[3] = { 0 ,0, 0 };
        FITKGraphCommons::QColorToDouble3(FITKGraphCommons::s_constraintColor, color3);

        // Create the actor for lines.
        m_fActor_2 = FITKGraphActor::New();
        m_fActor_2->setGraphObject(this);
        m_fActor_2->SetPickable(false);
        m_fActor_2->setScalarVisibility(false);
        m_fActor_2->GetProperty()->SetColor(color3);
        m_fActor_2->GetProperty()->SetLineWidth(2);

        // Add actors.
        addActor(m_fActor_2);
        //@}

        // Set the view mode for actor.
        m_fActor->GetProperty()->SetRepresentation(1);
        m_fActor_2->GetProperty()->SetRepresentation(1);
    }

    void FITKGraphObjectInteractionSurfToSurfExplicit::clearModelsInfo()
    {
        // Clear location one's model info.
        this->Superclass::clearModelsInfo();
        
        // Clear location two's model info.
        //@{
        // Clear model ids array.
        resetVtkObj(m_modelIdArray_2);
        //@}
    }

    void FITKGraphObjectInteractionSurfToSurfExplicit::clearData()
    {
        // Clear location two's data.
        //@{
        // Clear data.
        resetVtkObj(m_points_2);
        resetVtkObj(m_ugrid_2);
        //@}

        // Clear the normal for glyph.
        //@{
        resetVtkObj(m_normal);
        resetVtkObj(m_normal_2);
        //@}

        // Reset the shell surface flags.
        m_isShellSurfaceLoc_1 = false;
        m_isShellSurfaceLoc_2 = false;

        // Clear location one's data and clear model info at the same time.
        this->Superclass::clearData();
    }

    bool FITKGraphObjectInteractionSurfToSurfExplicit::generatePolyData(Interface::FITKInteractionLocation* interactions)
    {
        // Get the location id.
        Interface::FITKBoundaryLocation* location1 = interactions->getFirstLocation();
        Interface::FITKBoundaryLocation* location2 = interactions->getSecondLocation();
        if (!location1 || !location2)
        {
            return false;
        }

        // Get the locations' component id.
        m_locationCompId_1 = location1->getComponentID();
        m_locationCompId_2 = location2->getComponentID();

        // Try to get set and surface poly data.
        QList<int> modelIds_1, modelIds_2;
        QList<int> subSetIds_1, subSetIds_2;

        // Generate locations grid.
        generateComponentGridData(m_gridLoc_1, location1, modelIds_1, subSetIds_1, false);
        generateComponentGridData(m_gridLoc_2, location2, modelIds_2, subSetIds_2, false);

        // Save the model ids.
        m_modelIds_1 << modelIds_1;
        m_modelIds_2 << modelIds_2;

        // Save the sub set ids.
        m_subSetIds_1 << subSetIds_1;
        m_subSetIds_2 << subSetIds_2;

        // Save if the components are surface.
        int instanceId = -1, instanceId_2 = -1;
        Interface::FITKAbstractAssInstance* instance = location1->getTModelObj<Interface::FITKAbstractAssInstance>();
        if (instance)
        {
            instanceId = instance->getDataObjectID();
        }

        Interface::FITKAbstractAssInstance* instance_2 = location2->getTModelObj<Interface::FITKAbstractAssInstance>();
        if (instance_2)
        {
            instanceId_2 = instance_2->getDataObjectID();
        }

        // Check if the surface's cells type and label index.
        m_isShellSurfaceLoc_1 = FITKSetSurfPolyProvider::IsShellSurfaceNoneEdge(m_locationCompId_1, instanceId);
        m_isShellSurfaceLoc_2 = FITKSetSurfPolyProvider::IsShellSurfaceNoneEdge(m_locationCompId_2, instanceId_2);

        // Add surface1's points.
        //@{
        // Get the model id array of loc 1.
        vtkIntArray* modelIdArray_1 = vtkIntArray::SafeDownCast(m_gridLoc_1->GetCellData()->GetArray(FITKSetSurfPolyProvider::getInstance()->getModelIdsArrayName()));

        // Try to get the normal.
        vtkDoubleArray* normal_1 = vtkDoubleArray::SafeDownCast(m_gridLoc_1->GetCellData()->GetNormals());

        for (int i = 0; i < m_gridLoc_1->GetNumberOfCells(); i++)
        {
            int modelId = -1;
            if (modelIdArray_1)
            {
                modelId = modelIdArray_1->GetValue(i);
            }

            double pt[3]{ 0., 0., 0. };
            FITKGraphCommons::GetCellCenter(m_gridLoc_1->GetCell(i), pt);

            double normal[3]{ 0., 0., 1. };
            if (normal_1)
            {
                double* nor = normal_1->GetTuple3(i);
                normal[0] = nor[0];
                normal[1] = nor[1];
                normal[2] = nor[2];
            }

            // Add a point and a cell.
            //@{
            // Add point.
            int ptId = m_points->InsertNextPoint(pt);

            // Add cell.
            vtkSmartPointer<vtkIdList> cell = vtkSmartPointer<vtkIdList>::New();
            cell->InsertNextId(ptId);
            m_ugrid->InsertNextCell(VTKCellType::VTK_VERTEX, cell);

            // Add model id and normal.
            m_modelIdArray->InsertNextValue(modelId);
            m_normal->InsertNextTuple3(normal[0], normal[1], normal[2]);
            //@}
        }
        //@}

        // Get the model id array of loc 1.
        vtkIntArray* modelIdArray_2 = vtkIntArray::SafeDownCast(m_gridLoc_2->GetCellData()->GetArray(FITKSetSurfPolyProvider::getInstance()->getModelIdsArrayName()));

        // Try to get the normal.
        vtkDoubleArray* normal_2 = vtkDoubleArray::SafeDownCast(m_gridLoc_2->GetCellData()->GetNormals());

        for (int i = 0; i < m_gridLoc_2->GetNumberOfCells(); i++)
        {
            int modelId = -1;
            if (modelIdArray_2)
            {
                modelId = modelIdArray_2->GetValue(i);
            }

            double pt[3]{ 0., 0., 0. };
            FITKGraphCommons::GetCellCenter(m_gridLoc_2->GetCell(i), pt);

            double normal[3]{ 0., 0., 1. };
            if (normal_2)
            {
                double* nor = normal_2->GetTuple3(i);
                normal[0] = nor[0];
                normal[1] = nor[1];
                normal[2] = nor[2];
            }

            // Add a point and a cell.
            //@{
            // Add point.
            int ptId = m_points_2->InsertNextPoint(pt);

            // Add cell.
            vtkSmartPointer<vtkIdList> cell = vtkSmartPointer<vtkIdList>::New();
            cell->InsertNextId(ptId);
            m_ugrid_2->InsertNextCell(VTKCellType::VTK_VERTEX, cell);

            // Add model id and normal.
            m_modelIdArray_2->InsertNextValue(modelId);
            m_normal_2->InsertNextTuple3(normal[0], normal[1], normal[2]);
            //@}
        }
        //@}

        return true;
    }

    void FITKGraphObjectInteractionSurfToSurfExplicit::setVisible(bool visibility)
    {
        // Hide the contact 1.
        this->Superclass::setVisible(visibility);

        // Hide the contact 2.
        if (m_fActor_2)
        {
            m_fActor_2->SetVisibility(visibility);
        }
    }

    void FITKGraphObjectInteractionSurfToSurfExplicit::updateVisibility()
    {
        // Update the full actor.
        FITKGraphObject3D::updateVisibility();

        if (!m_idArray || !m_modelIdArray || !m_ugrid || !m_idArray_2 || !m_modelIdArray_2 || !m_ugrid_2)
        {
            return;
        }

        QHash<int, bool> modelVisibilityHash;

        // Save the visibility first, and check if the visbility has changed at the same time.
        bool hasChanged = false;
        for (int modelId : m_modelIds_1 + m_modelIds_2)
        {
            bool parentVisible = getParentDataVisibility(modelId);
            bool oldVisibility = false;
            if (m_modelVisibilityHash.contains(modelId))
            {
                oldVisibility = m_modelVisibilityHash[modelId];
            }

            if (parentVisible != oldVisibility)
            {
                hasChanged = true;
            }

            if (!modelVisibilityHash.contains(modelId))
            {
                modelVisibilityHash.insert(modelId, parentVisible);
            }
        }

        // If the visibility hasn't changed, then return.
        if (!hasChanged && !m_modelVisibilityHash.isEmpty())
        {
            return;
        }

        // Reset the extractor's ids.
        resetVtkObj(m_idArray);
        resetVtkObj(m_idArray_2);

        // Update the extractor one's ids.
        for (int i = 0; i < m_ugrid->GetNumberOfCells(); i++)
        {
            bool visibility = getCellVisibilityWithModel(m_ugrid->GetCell(i), m_modelIdArray, modelVisibilityHash);
            if (visibility)
            {
                m_idArray->InsertNextValue(i);
            }
        }

        // Update the extractor two's ids.
        for (int i = 0; i < m_ugrid_2->GetNumberOfCells(); i++)
        {
            bool visibility = getCellVisibilityWithModel(m_ugrid_2->GetCell(i), m_modelIdArray_2, modelVisibilityHash);
            if (visibility)
            {
                m_idArray_2->InsertNextValue(i);
            }
        }

        // Update the data.
        m_ugrid->Modified();
        m_ugrid_2->Modified();

        // Save the flags.
        m_modelVisibilityHash = modelVisibilityHash;
    }

    void FITKGraphObjectInteractionSurfToSurfExplicit::simplified(bool enabled)
    {
        if (!m_graphSimplified)
        {
            return;
        }

        bool dataVisible = getDataVisibility();

        // Change the actor visibility while changing the camera.
        if (dataVisible)
        {
            if (m_fActor)
            {
                m_fActor->SetVisibility(!enabled);
            }

            if (m_fActor_2)
            {
                m_fActor_2->SetVisibility(!enabled);
            }
        }
    }

    void FITKGraphObjectInteractionSurfToSurfExplicit::highlight(QColor color)
    {
        // If the object is invisible, then return.
        if (!getDataVisibility())
        {
            return;
        }

        // Get the interactions highlight color.
        double color3[3] = { 0 ,0, 0 };
        if (color.isValid())
        {
            FITKGraphCommons::QColorToDouble3(color, color3);
        }
        else
        {
            FITKGraphCommons::QColorToDouble3(FITKGraphCommons::s_interactionHighlightColor, color3);
        }       

//#ifdef QT_NO_DEBUG
        if (!m_simplifiedGlyph)
        {
            // Change the glyph source for shell surface or highlight the glyph.
            //@{
            if (m_isShellSurfaceLoc_1 && m_glyph && m_distanceToCamera && m_surfaceSource)
            {
                m_glyph->SetSourceConnection(m_surfaceSource->GetOutputPort());
                m_glyph->SetVectorModeToUseNormal();
                m_distanceToCamera->SetScreenSize(m_highlightGlyphSize);
            }
            else
            {
                if (m_fActor)
                {
                    m_fActor->GetProperty()->SetColor(color3);
                }
            }

            if (m_isShellSurfaceLoc_2 && m_glyph_2 && m_distanceToCamera_2 && m_surfaceSource)
            {
                m_glyph_2->SetSourceConnection(m_surfaceSource->GetOutputPort());
                m_glyph_2->SetVectorModeToUseNormal();
                m_distanceToCamera_2->SetScreenSize(m_highlightGlyphSize);
            }
            else
            {
                if (m_fActor_2)
                {
                    m_fActor_2->GetProperty()->SetColor(color3);
                }
            }
            //@}
        }
        else
        {
            // Change the glyph color.
            if (m_fActor)
            {
                m_fActor->GetProperty()->SetColor(color3);
            }

            if (m_fActor_2)
            {
                m_fActor_2->GetProperty()->SetColor(color3);
            }
        }
//#endif

        FITKGraphObject3D::highlight();
    }

    void FITKGraphObjectInteractionSurfToSurfExplicit::disHighlight()
    {
        // Get the interactions or constraint default color.
        double color3[3] = { 0 ,0, 0 };
        FITKGraphCommons::QColorToDouble3(FITKGraphCommons::s_constraintColor, color3);

//#ifdef QT_NO_DEBUG
        if (!m_simplifiedGlyph)
        {
            // Change the glyph source for shell surface or reset the highlight color.
            //@{
            if (m_glyph && m_distanceToCamera)
            {
                m_glyph->SetSourceConnection(m_polygonSource->GetOutputPort());
                m_glyph->SetVectorModeToVectorRotationOff();
                m_distanceToCamera->SetScreenSize(m_glyphSize);
            }
            if (m_glyph_2 && m_distanceToCamera_2)
            {
                m_glyph_2->SetSourceConnection(m_polygonSource->GetOutputPort());
                m_glyph_2->SetVectorModeToVectorRotationOff();
                m_distanceToCamera_2->SetScreenSize(m_glyphSize);
            }
        }
//#endif

        // Reset the color.
        if (m_fActor)
        {
            m_fActor->GetProperty()->SetColor(color3);
        }

        if (m_fActor_2)
        {
            m_fActor_2->GetProperty()->SetColor(color3);
        }

        FITKGraphObject3D::disHighlight();
    }

    void FITKGraphObjectInteractionSurfToSurfExplicit::generateGraph()
    {
        if (!m_ugrid || !m_ugrid_2 || !m_normal || !m_normal_2)
        {
            return;
        }

        // Add the normal to point data.
        m_ugrid->GetPointData()->SetNormals(m_normal);
        m_ugrid_2->GetPointData()->SetNormals(m_normal_2);

        // Initialize the actors.
        initActors();

        // Create the glyph filter.
        m_glyph = vtkGlyph3D::New();
        m_glyph->SetScaling(true);
        m_glyph->SetScaleModeToScaleByScalar();
        m_glyph->SetVectorModeToVectorRotationOff();

        m_glyph_2 = vtkGlyph3D::New();
        m_glyph_2->SetScaling(true);
        m_glyph_2->SetScaleModeToScaleByScalar();
        m_glyph_2->SetVectorModeToVectorRotationOff();

        // Add to data list.
        m_glyphList.push_back(m_glyph);
        m_glyphList.push_back(m_glyph_2);

        m_fActor->setInputConnection(m_glyph->GetOutputPort());
        m_fActor_2->setInputConnection(m_glyph_2->GetOutputPort());

//#ifdef QT_NO_DEBUG
        if (!m_simplifiedGlyph)
        {
            // m_fActor->GetProperty()->SetPointSize(m_glyphSize);
            m_fActor->GetProperty()->SetLineWidth(1);
            m_fActor_2->GetProperty()->SetLineWidth(1);

            // For none-fixed size.
            //@{
            m_distanceToCamera = vtkDistanceToCamera::New();
            m_distanceToCamera->SetScreenSize(m_glyphSize);

            m_distanceToCamera_2 = vtkDistanceToCamera::New();
            m_distanceToCamera_2->SetScreenSize(m_glyphSize);

            // Set the input.
            if (m_useMaskPoints)
            {
                // Initialize the mask points.
                vtkMaskPoints* maskPoints = vtkMaskPoints::New();
                FITKGraphCommons::InitializeMaskPoints(maskPoints, m_extractSelection->GetOutputPort(), m_maxSampleNumber);

                vtkMaskPoints* maskPoints_2 = vtkMaskPoints::New();
                FITKGraphCommons::InitializeMaskPoints(maskPoints_2, m_extractSelection->GetOutputPort(), m_maxSampleNumber);

                m_maskPointsList.push_back(maskPoints);
                m_maskPointsList.push_back(maskPoints_2);

                m_distanceToCamera->SetInputConnection(maskPoints->GetOutputPort());
                m_distanceToCamera_2->SetInputConnection(maskPoints_2->GetOutputPort());
            }
            else
            {
                m_distanceToCamera->SetInputConnection(m_extractSelection->GetOutputPort());
                m_distanceToCamera_2->SetInputConnection(m_extractSelection_2->GetOutputPort());
            }

            // Add the point array name need to be used.
#if VTK_MAJOR_VERSION < 8
            m_glyph->SetInputArrayToProcess(0, 0, 0, vtkDataObject::FIELD_ASSOCIATION_POINTS, "DistanceToCamera");
            m_glyph_2->SetInputArrayToProcess(0, 0, 0, vtkDataObject::FIELD_ASSOCIATION_POINTS, "DistanceToCamera");
#else
            m_glyph->SetInputArrayToProcess(0, 0, 0, vtkDataObject::FIELD_ASSOCIATION_POINTS, m_distanceToCamera->GetDistanceArrayName());
            m_glyph_2->SetInputArrayToProcess(0, 0, 0, vtkDataObject::FIELD_ASSOCIATION_POINTS, m_distanceToCamera_2->GetDistanceArrayName());
#endif

            m_glyph->SetInputConnection(m_distanceToCamera->GetOutputPort());
            m_glyph_2->SetInputConnection(m_distanceToCamera_2->GetOutputPort());

            // Add to data list.
            m_distToCameraList.push_back(m_distanceToCamera);
            m_distToCameraList.push_back(m_distanceToCamera_2);

            // Set the soure data and input data.
            m_glyph->SetSourceConnection(m_polygonSource->GetOutputPort());
            m_glyph_2->SetSourceConnection(m_polygonSource->GetOutputPort());
            //@}
        }
        else
        {
            m_fActor->GetProperty()->SetPointSize(m_glyphSize);
            m_fActor_2->GetProperty()->SetPointSize(m_glyphSize);

            // Set the input poly data.
                        // Set the input.
            if (m_useMaskPoints)
            {
                // Initialize the mask points.
                vtkMaskPoints* maskPoints = vtkMaskPoints::New();
                FITKGraphCommons::InitializeMaskPoints(maskPoints, m_extractSelection->GetOutputPort(), m_maxSampleNumber);

                vtkMaskPoints* maskPoints_2 = vtkMaskPoints::New();
                FITKGraphCommons::InitializeMaskPoints(maskPoints_2, m_extractSelection->GetOutputPort(), m_maxSampleNumber);

                m_maskPointsList.push_back(maskPoints);
                m_maskPointsList.push_back(maskPoints_2);

                m_glyph->SetInputConnection(maskPoints->GetOutputPort());
                m_glyph_2->SetInputConnection(maskPoints_2->GetOutputPort());
            }
            else
            {
                m_glyph->SetInputConnection(m_extractSelection->GetOutputPort());
                m_glyph_2->SetInputConnection(m_extractSelection_2->GetOutputPort());
            }

            // Set the soure data and input connection.
            m_glyph->SetSourceConnection(m_pointSource->GetOutputPort());
            m_glyph_2->SetSourceConnection(m_pointSource->GetOutputPort());
        }
//#endif // QT_NO_DEBUG
    }
}   // namespace Exchange